// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace System.Net.Http
{
    public abstract class DelegatingHandler : HttpMessageHandler
    {
        private HttpMessageHandler? _innerHandler;
        private volatile bool _operationStarted;
        private volatile bool _disposed;

        [DisallowNull]
        public HttpMessageHandler? InnerHandler
        {
            get
            {
                return _innerHandler;
            }
            set
            {
                ArgumentNullException.ThrowIfNull(value);
                CheckDisposedOrStarted();

                if (NetEventSource.Log.IsEnabled()) NetEventSource.Associate(this, value);
                _innerHandler = value;
            }
        }

        protected DelegatingHandler()
        {
        }

        protected DelegatingHandler(HttpMessageHandler innerHandler)
        {
            InnerHandler = innerHandler;
        }

        protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            ArgumentNullException.ThrowIfNull(request);

            SetOperationStarted();
            return _innerHandler!.Send(request, cancellationToken);
        }

        protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            ArgumentNullException.ThrowIfNull(request);

            SetOperationStarted();
            return _innerHandler!.SendAsync(request, cancellationToken);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && !_disposed)
            {
                _disposed = true;
                _innerHandler?.Dispose();
            }

            base.Dispose(disposing);
        }

        private void CheckDisposedOrStarted()
        {
            ObjectDisposedException.ThrowIf(_disposed, this);

            if (_operationStarted)
            {
                throw new InvalidOperationException(SR.net_http_operation_started);
            }
        }

        private void SetOperationStarted()
        {
            ObjectDisposedException.ThrowIf(_disposed, this);

            if (_innerHandler == null)
            {
                throw new InvalidOperationException(SR.net_http_handler_not_assigned);
            }
            // This method flags the handler instances as "active". I.e. we executed at least one request (or are
            // in the process of doing so). This information is used to lock-down all property setters. Once a
            // Send/SendAsync operation started, no property can be changed.
            if (!_operationStarted)
            {
                _operationStarted = true;
            }
        }
    }
}
